package com.jiaomatech.tencent.cos.resource;

import com.qcloud.cos.COSClient;
import com.qcloud.cos.model.Bucket;
import com.qcloud.cos.model.COSObject;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;

/**
 * Description: 访问cos资源的实现类 .<br>
 *
 * @author jm
 * @see cos
 * @see Bucket
 * @see cosObject
 */
public class CosStorageResource implements Resource {

    private final COSClient cos;
    private final String bucketName;
    private final String objectKey;
    private final URI location;

    public CosStorageResource(COSClient cos, String location) {
        Assert.notNull(cos, "COS 的连接不能为空");
        Assert.isTrue(location.startsWith(CosStorageProtocolResolver.PROTOCOL),
            "必须以此为开头: " + CosStorageProtocolResolver.PROTOCOL);
        this.cos = cos;
        try {
            URI locationUri = new URI(location);
            this.bucketName = locationUri.getAuthority();

            if (locationUri.getPath() != null && locationUri.getPath().length() > 1) {
                this.objectKey = locationUri.getPath().substring(1);
            } else {
                this.objectKey = null;
            }
            this.location = locationUri;
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException("Invalid location: " + location, e);
        }
    }

    /**
     * 返回Resource所指的资源是否存在.
     *
     * @return 是否.
     */
    @Override
    public boolean exists() {
        try {
            return isBucket() ? getBucket() != null : getCOSObject() != null;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 返回资源对应的URL对象.
     *
     * @return
     * @throws IOException
     */
    @Override
    public URL getURL() throws IOException {
        return this.location.toURL();
    }

    /**
     * 返回资源对应的URI对象.
     *
     * @return
     * @throws IOException
     */
    @Override
    public URI getURI() throws IOException {
        return this.location;
    }

    /**
     * 返回资源对应的File对象.
     *
     * @return
     * @throws IOException
     */
    @Override
    public File getFile() throws IOException {
        throw new UnsupportedOperationException(
            getDescription() + " 无法解析为绝对文件路径.");
    }

    @Override
    public long contentLength() throws IOException {
        assertExisted();
        if (isBucket()) {
            throw new FileNotFoundException("cosObject 不存在.");
        }
        return getCOSObject().getObjectMetadata().getContentLength();
    }

    @Override
    public long lastModified() throws IOException {
        assertExisted();
        if (isBucket()) {
            throw new FileNotFoundException("cosObject 不存在.");
        }
        return getCOSObject().getObjectMetadata().getLastModified().getTime();
    }

    @Override
    public Resource createRelative(String relativePath) throws IOException {
        return new CosStorageResource(this.cos,
            this.location.resolve(relativePath).toString());
    }

    /**
     * 返回资源名称，如果是 Bucket 则返回 bucketName；如果不是 Bucket 则返回 objectKey.
     *
     * @return
     */
    @Override
    public String getFilename() {
        return isBucket() ? this.bucketName : this.objectKey;
    }

    /**
     * 返回资源的描述星星，用于资源处理出错时输出该信息，通常是全限定名或实际URL.
     *
     * @return
     */
    @Override
    public String getDescription() {
        return this.location.toString();
    }

    /**
     * 定位打开资源，返回资源对应的输入流，每次调用都返回新的输入流，调用者必须负责关闭输入流.
     *
     * @return
     * @throws IOException
     */
    @Override
    public InputStream getInputStream() throws IOException {
        assertExisted();
        if (isBucket()) {
            throw new IllegalStateException(
                "Cannot open an input stream to a bucket: '" + this.location + "'");
        } else {
            return getCOSObject().getObjectContent();
        }
    }

    /**
     * 得到制定名称的 Bucket.
     */
    public Bucket getBucket() {
        return this.cos.listBuckets().stream()
            .filter(bucket -> bucket.getName().equals(this.bucketName)).findFirst()
            .get();
    }

    /**
     * Bucket 是否存在.
     */
    public boolean bucketExists() {
        return getBucket() != null;
    }

    /**
     * 从腾讯云 COS 上拿到资源.
     *
     * @throws cosException
     * @throws ClientException
     */
    public COSObject getCOSObject() {
        return this.cos.getObject(this.bucketName, this.objectKey);
    }

    /**
     * 键 是否为空 .
     */
    public boolean isBucket() {
        return this.objectKey == null;
    }

    private void assertExisted() throws FileNotFoundException {
        if (!exists()) {
            throw new FileNotFoundException("Bucket 或者 cosObject 不存在.");
        }
    }

}
